; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=aggressive-instcombine -S -data-layout="e" | FileCheck %s --check-prefixes=CHECK,LE
; RUN: opt < %s -passes=aggressive-instcombine -S -data-layout="E" | FileCheck %s --check-prefixes=CHECK,BE


@constarray1 = internal constant [8 x i8]  c"\01\00\01\00\01\00\01\00", align 4
@constarray2 = internal constant [8 x i8]  c"\FF\FF\01\00\01\00\01\00", align 4

@g = internal constant i32 42
@constptrarray = internal constant [4 x ptr]  [ptr @g, ptr @g, ptr @g, ptr @g], align 4

@constpackedstruct = internal constant <{[8 x i8]}>  <{[8 x i8] c"\01\00\01\00\01\00\01\00"}>, align 4
@conststruct = internal constant {i16, [8 x i8]}  {i16 1, [8 x i8] c"\01\00\01\00\01\00\01\00"}, align 4

define i8 @inbounds_gep_load_i8_align2(i64 %idx){
; CHECK-LABEL: @inbounds_gep_load_i8_align2(
; CHECK-NEXT:    ret i8 1
;
  %1 = getelementptr inbounds i8, ptr @constarray1, i64 %idx
  %2 = load i8, ptr %1, align 2
  ret i8 %2
}

; can't be folded because access with i8 strides is not patterned.
define i8 @inbounds_gep_load_i8_align1(i64 %idx){
; CHECK-LABEL: @inbounds_gep_load_i8_align1(
; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i8, ptr @constarray1, i64 [[IDX:%.*]]
; CHECK-NEXT:    [[TMP2:%.*]] = load i8, ptr [[TMP1]], align 1
; CHECK-NEXT:    ret i8 [[TMP2]]
;
  %1 = getelementptr inbounds i8, ptr @constarray1, i64 %idx
  %2 = load i8, ptr %1, align 1
  ret i8 %2
}

; can't be folded because volatile load cannot assure same results.
define i8 @inbounds_gep_load_i8_align2_volatile(i64 %idx){
; CHECK-LABEL: @inbounds_gep_load_i8_align2_volatile(
; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i8, ptr @constarray1, i64 [[IDX:%.*]]
; CHECK-NEXT:    [[TMP2:%.*]] = load volatile i8, ptr [[TMP1]], align 2
; CHECK-NEXT:    ret i8 [[TMP2]]
;
  %1 = getelementptr inbounds i8, ptr @constarray1, i64 %idx
  %2 = load volatile i8, ptr %1, align 2
  ret i8 %2
}

declare ptr @llvm.ptrmask.p0.i64(ptr , i64)

; can't be folded because ptrmask can change ptr, while preserving provenance
define i8 @inbounds_gep_load_i8_align2_ptrmasked(i64 %idx, i64 %mask){
; CHECK-LABEL: @inbounds_gep_load_i8_align2_ptrmasked(
; CHECK-NEXT:    ret i8 1
;
  %1 = call ptr @llvm.ptrmask.p0.i64(ptr @constarray1, i64 %mask)
  %2 = getelementptr inbounds i8, ptr %1, i64 %idx
  %3 = load i8, ptr %2, align 2
  ret i8 %3
}

; TODO: this will be ret i32 65537(LE), 16777472(BE)
define i32 @inbounds_gep_i16_load_i32_align1(i64 %idx){
; CHECK-LABEL: @inbounds_gep_i16_load_i32_align1(
; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i16, ptr @constarray1, i64 [[IDX:%.*]]
; CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 1
; CHECK-NEXT:    ret i32 [[TMP2]]
;
  %1 = getelementptr inbounds i16, ptr @constarray1, i64 %idx
  %2 = load i32, ptr %1, align 1
  ret i32 %2
}

; TODO: this will be ret i32 65537(LE), 16777472(BE)
define i32 @inbounds_gep_i32_load_i32_align8(i64 %idx){
; CHECK-LABEL: @inbounds_gep_i32_load_i32_align8(
; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i32, ptr @constarray1, i64 [[IDX:%.*]]
; CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 8
; CHECK-NEXT:    ret i32 [[TMP2]]
;
  %1 = getelementptr inbounds i32, ptr @constarray1, i64 %idx
  %2 = load i32, ptr %1, align 8
  ret i32 %2
}

; TODO: this will be ret i32 65547(LE), 16777472(BE)
define i32 @inbounds_gep_i32_load_i32_const_offset(i64 %idx){
; CHECK-LABEL: @inbounds_gep_i32_load_i32_const_offset(
; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i16, ptr @constarray2, i64 1
; CHECK-NEXT:    [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX:%.*]]
; CHECK-NEXT:    [[TMP3:%.*]] = load i32, ptr [[TMP2]], align 4
; CHECK-NEXT:    ret i32 [[TMP3]]
;
  %1 = getelementptr inbounds i16, ptr @constarray2, i64 1
  %2 = getelementptr inbounds i32, ptr %1, i64 %idx
  %3 = load i32, ptr %2, align 4
  ret i32 %3
}

define i32 @gep_load_i32_align2_const_offset(i64 %idx){
; LE-LABEL: @gep_load_i32_align2_const_offset(
; LE-NEXT:    ret i32 65537
;
; BE-LABEL: @gep_load_i32_align2_const_offset(
; BE-NEXT:    ret i32 16777472
;
  %1 = getelementptr i16, ptr @constarray1, i64 -2
  %2 = getelementptr [3 x i16], ptr %1, i64 %idx
  %3 = load i32, ptr %2, align 2
  ret i32 %3
}

; can't be folded because if gep is non-inbounds,
; the offsets are silently-wrapped with two’s complement arithmetic(mod 2**64).
; So the load operand can be a base pointer of constarray2.
define i32 @gep_load_i32_align2_const_offset_wrap(i64 %idx){
; CHECK-LABEL: @gep_load_i32_align2_const_offset_wrap(
; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i16, ptr @constarray2, i64 -2
; CHECK-NEXT:    [[TMP2:%.*]] = getelementptr [3 x i16], ptr [[TMP1]], i64 [[IDX:%.*]]
; CHECK-NEXT:    [[TMP3:%.*]] = load i32, ptr [[TMP2]], align 2
; CHECK-NEXT:    ret i32 [[TMP3]]
;
  %1 = getelementptr i16, ptr @constarray2, i64 -2
  %2 = getelementptr [3 x i16], ptr %1, i64 %idx
  %3 = load i32, ptr %2, align 2
  ret i32 %3
}

; TODO: this will be ret i32 42
define i32 @inbounds_gep_i32_load_i32_const_ptr_array(i64 %idx){
; CHECK-LABEL: @inbounds_gep_i32_load_i32_const_ptr_array(
; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds ptr, ptr @constptrarray, i64 [[IDX:%.*]]
; CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[TMP1]], align 4
; CHECK-NEXT:    [[TMP3:%.*]] = load i32, ptr [[TMP2]], align 4
; CHECK-NEXT:    ret i32 [[TMP3]]
;
  %1 = getelementptr inbounds ptr, ptr @constptrarray, i64 %idx
  %2 = load ptr, ptr %1, align 4
  %3 = load i32, ptr %2, align 4
  ret i32 %3
}

define i32 @inbounds_gep_i32_load_i32_align4_packedstruct(i64 %idx){
; LE-LABEL: @inbounds_gep_i32_load_i32_align4_packedstruct(
; LE-NEXT:    ret i32 65537
;
; BE-LABEL: @inbounds_gep_i32_load_i32_align4_packedstruct(
; BE-NEXT:    ret i32 16777472
;
  %1 = getelementptr inbounds i32, ptr @constpackedstruct, i64 %idx
  %2 = load i32, ptr %1, align 4
  ret i32 %2
}

; can't be folded because results are not equal
define i32 @inbounds_gep_i8_load_i32_align1_packedstruct(i64 %idx){
; CHECK-LABEL: @inbounds_gep_i8_load_i32_align1_packedstruct(
; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i8, ptr @constpackedstruct, i64 [[IDX:%.*]]
; CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 1
; CHECK-NEXT:    ret i32 [[TMP2]]
;
  %1 = getelementptr inbounds i8, ptr @constpackedstruct, i64 %idx
  %2 = load i32, ptr %1, align 1
  ret i32 %2
}

; TODO: this coould be folded into 65537(LE), 16777472(BE)
define i32 @inbounds_gep_i32_load_i32_align4_struct_with_const_offset(i64 %idx){
; LE-LABEL: @inbounds_gep_i32_load_i32_align4_struct_with_const_offset(
; LE-NEXT:    ret i32 65537
;
; BE-LABEL: @inbounds_gep_i32_load_i32_align4_struct_with_const_offset(
; BE-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i16, ptr @conststruct, i64 1
; BE-NEXT:    [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[IDX:%.*]]
; BE-NEXT:    [[TMP3:%.*]] = load i32, ptr [[TMP2]], align 4
; BE-NEXT:    ret i32 [[TMP3]]
;
  %1 = getelementptr inbounds i16, ptr @conststruct, i64 1
  %2 = getelementptr inbounds i32, ptr %1, i64 %idx
  %3 = load i32, ptr %2, align 4
  ret i32 %3
}

